#ifndef TYPED_AST_HPP
#define TYPED_AST_HPP

#include "visitor.hpp"

#include <string>
using namespace std;

class SymbolTable;
class TypeTable;
class Type;

class RecordType;
class TraitType;

class TypedVisitor : public Visitor {
private:
    SymbolTable* _sym_table;

    bool _has_error;
    bool _is_func;

public:
    TypedVisitor(SymbolTable* symtable);

    ~TypedVisitor();

    bool has_error() { return _has_error; }

    virtual Node* visit(Node* n);

    virtual Node* visit(PrintNode* n);
    virtual Node* visit(ReturnNode* n);
    virtual Node* visit(BinaryOp* n);
    virtual Node* visit(LogicNotNode* n);
    virtual Node* visit(ListNode* n);
    virtual Node* visit(StmtsNode* n);
    virtual Node* visit(ConstInt* n);
    virtual Node* visit(ConstDouble* n);
    virtual Node* visit(ConstString* n);
    virtual Node* visit(ConstArray* n);
    virtual Node* visit(ImportNode* n);
    virtual Node* visit(ImplementNode* n);
    virtual Node* visit(IfNode* n);
    virtual Node* visit(WhileNode* n);
    virtual Node* visit(CmpNode* n);
    virtual Node* visit(AssignNode* n);
    virtual Node* visit(VarNode* n);
    virtual Node* visit(VarDefNode* n);
    virtual Node* visit(ParamDefNode* n);
    virtual Node* visit(FuncDefNode* n);
    virtual Node* visit(RecordDefNode* n);
    virtual Node* visit(TraitDefNode* n);
    virtual Node* visit(LambdaDef* n);
    virtual Node* visit(CallNode* n);
    virtual Node* visit(SubscrNode* n);
    virtual Node* visit(DotNode* n);
    virtual Node* visit(SequenceNode* n);
    virtual Node* visit(BuiltinConst* n);
    virtual Node* visit(TupleNode* n);

    virtual Node* visit(ArrowTypeNode* n);
    virtual Node* visit(ArrayTypeNode* n);

    virtual Node* visit(TypeVarNode* n);
    virtual Node* visit(TypeFuncDefNode* n);
    virtual Node* visit(TypeVarDefNode* n);
    virtual Node* visit(TypeCallNode* n);
    virtual Node* visit(TypeConstInt* n);
    virtual Node* visit(TypeTupleNode* n);

    Type* build_func_type(FuncDefNode* n);
    Type* get_param_type(ParamDefNode* n);
    void add_method_to_record(RecordDefNode* record_def_node, 
        FuncDefNode* func_def_node, TraitType* trait_type);

    void check_name(const string& name, Type* t);
    void update_name_type(const string& name, Type* t);

    void enter_type_scope();
    void exit_type_scope();

    void add_free_var(SymbolTable* pst, const string& name, Type* type);

    TypeTable* type_table();
};

#endif